//
//  PSHand.h
//  PrefCore
//
//  Created by Sergey on 2/10/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "PSGameSettings.h"
#import "PSCard.h"
#import "PSTrick.h"

@class PSGame;

typedef uint32_t PSHandHash;

/// Get hash value containing only cards from the given suit moved to the LSB.
inline PSCardRankSet PSHandGetSuit(PSHandHash hand, PSCardSuit suit) {
    return (hand >> PSCardSuitPower(suit)) & 0xFF;
}

/// Add all cards from the set, with the given suit.
inline PSHandHash PSHandAddSuitFromSet(PSHandHash hand, PSCardRankSet cards, PSCardSuit suit) {
    return hand | (((PSHandHash)cards) << PSCardSuitPower(suit));
}

/// Get hash value containing only cards from the given suit not moved.
inline PSHandHash PSHandKeepSuit(PSHandHash hand, PSCardSuit suit) {
    return hand & (0xFF << PSCardSuitPower(suit));
}

/// Add all cards from the other hand.
inline PSHandHash PSHandAddHand(PSHandHash one, PSHandHash two) {
    return one | two;
}

/// Add all cards from an array
extern PSHandHash PSHandAddCards(PSHandHash hand, const PSCardHash* cards, size_t count);

/// Add all cards in the given suit from the other hand.
inline PSHandHash PSHandAddSuit(PSHandHash hand, PSCardSuit suit, PSHandHash otherHand) {
    return hand | PSHandKeepSuit(otherHand, suit);
}

/// Makes a hand from a single card
inline PSHandHash PSHandWithOneCard(const PSCard *card) {
    return card.hash32;
}

/// Add a card to the hand.
inline PSHandHash PSHandAddCard(PSHandHash hand, const PSCard *card) {
    return hand | card.hash32;
}

inline PSHandHash PSHandRemaining(PSHandHash cards[3]) {
    return ~(cards[0] | cards[1] | cards[2]);
}

/// Remove a card from the hand.
inline PSHandHash PSHandRemoveHand(PSHandHash hand, PSHandHash otherHand) {
    return hand & ~otherHand;
}

/// Remove all cards from an array of hands
extern PSHandHash PSHandRemoveCards(PSHandHash hand, const PSCardHash *cards, size_t count);

/// Remove the whole suit
inline PSHandHash PSHandRemoveSuit(PSHandHash hand, PSCardSuit suit) {
    return hand & ~(0xFF << PSCardSuitPower(suit));
}

/// Check if a hand contains a specific card.
inline BOOL PSHandContainsCard(PSHandHash hand, PSCardHash cardHash) {
    return (hand & cardHash) != 0;
}

/// Check if a hand contains all cards from the other hand,
inline BOOL PSHandContainsAllCards(PSHandHash hand, PSHandHash otherHand) {
    return (hand & otherHand) == otherHand;
}

/// Adjust the hand by adding a talon and removing the drop.
inline PSHandHash PSHandAdjust(PSHandHash hand, const PSCardHash *talon, PSCardHash *drop) {
    return PSHandRemoveCards(PSHandAddCards(hand, talon, 2), drop, 2);
}

/// Get the first card in the hand.
extern PSCardHash PSHandGetFirstCard(PSHandHash hand);

/// Get the last card in the hand.
extern PSCardHash PSHandGetLastCard(PSHandHash hand);

/// Get the next card in the hand.
extern PSCardHash PSHandGetNextCard(PSHandHash hand, PSCardHash current);

/// Get the previous card in the hand.
extern PSCardHash PSHandGetPreviousCard(PSHandHash hand, PSCardHash current);

/// Get the size of the hand
inline PSCardHash PSHandGetSize(PSHandHash hand) {
    return __builtin_popcount(hand);
}

/// Get the number of suits present in the hand.
extern uint8_t PSHandSuitCount(PSHandHash hand);

extern PSHandHash PSHandAddTrick(PSHandHash hand, PSTrickHash trick);

/// Get potential candidates for a move.
extern PSHandHash PSHandMoveCandidates(PSHandHash hand, PSTrickHash trick, PSCardSuit trump);

/// Get potential candidates for a move, at the current state of the game.
extern PSHandHash PSHandMoveCandidatesInGame(PSGame *game, PSHandHash hand);

/// Get all cards ordered according to the requested rank order.
extern NSArray* PSHandGetCards(PSHandHash hand, NSArray *suits, PSGameRankOrder rankOrder, BOOL sort);

/// Get all suits from the current hand, ordered according to suit order.
extern NSArray* PSHandOrderedSuits(PSHandHash hand, PSGameSuitOrder order);

/// Produces a textual representation of the hand.
extern NSString *PSHandDescription(PSHandHash hand);
